﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace SimpleCompiler
{
    public class ParseResult
    {
        public Statement statement = new Statement();
        public Dictionary<string, StatementFunction> dicParsedFunctions = new Dictionary<string, StatementFunction>();
    }

    /// <summary>
    /// 
    /// SOURCE_FILE = (FUNCTION | STATEMENT) *
    ///
    /// FUNCTION = "fun" + FUNCTION_NAME + (PARAM*){ (STATEMENT;)*}
    /// 
    /// STATEMENT = SIMPLE_STATEMENT | CONTROL_FLOW_STATEMENT
    /// 
    /// SIMPLE_STATEMENT = 
    ///             1. TYPE VARIABLE_NAME;   // 变量定义
    ///             2. VARIABLE_NAME = VARIABLE + OP + VARIABLE; // a= b + 1
    ///             3. VARIABLE + OP + VARIABLE;       // a>b    
    ///             4. "return" VARIABLE;
    ///             5. "break";
    ///             6. FUNCTION_NAME((VARIABLE,)*)
    ///             7. VARIABLE_NAME = FUNCTION_NAME((VARIABLE,)*)
    ///             8. VARiABLE_NAME = VARIABLE
    ///             11. {
    ///             12. }
    /// CONTROL_FLOW_STATEMENT
    ///             9. "for" + "(" + SIMPLE_STATEMENT + "," + "SIMPLE_STATEMENT" + "," + "SIMPLE_STATEMENT") + "{" + (STATEMENT;)* + "}"   // for
    ///             10. "if"  + "(" + SIMPLE_STATEMENT + ")" + "{" + (STATEMENT;)* + "}"  + "else" + "{" + (STATEMENT;)* + "}"
    /// 
    /// TYPE = "int" | "double" | "string" | "bool"
    /// VARIABLE_NAME = (A_Z|a_z)+
    /// VARIABLE = <VARIABLE_NAME> | <NUMBER> | <STRING> | true | false
    /// STRING = " + (A_Z|a_z) + "
    /// NUMBER = - | 0 | ((1-9) + (0-9)*)
    /// PARAM = TYPE VARIABLE_NAME
    /// OP = + | - | * | / | > | >= | < | <= | == | =
    ///            
    /// 
    /// </summary>
    public class Parser
    {
        string souce_content = "fun abc(int a, int b)"
        + "{"
        + "     int result"
        + "     result = 1;"
        + "     int i;"
        + "     for (i = 0; i < a; i ++)"
        + "     {"
        + "         result = result * b;"
        + "         if (result > 100)"
        + "         {"
        + "             return result;"
        + "         }"
        + "     }"
        + "     return 1;"
        + "}"
        + "int a"
        + "a = abc(5, 7)";

        public Parser(string souceContent = "")
        {
            if (souceContent.Length > 0)
            {
                this.souce_content = souceContent;
            }
        }

        
        class TokenVisitor
        {
            int used_count = 0;
            public List<string> tokens = new List<string>();

            public string DebugNextTokens => $"{peek(0)} {peek(1)} {peek(2)}";

            public int totalCount => tokens.Count;
            public int remainCount => tokens.Count - used_count;
            public string peek(int skip=0)
            {
                if (used_count == tokens.Count)
                {
                    return "";
                }
                return tokens[used_count + skip];
            }

            public string next()
            {
                if (used_count == tokens.Count)
                {
                    return "";
                }
                used_count++;
                return tokens[used_count - 1];
            }
        }

        private BinaryOperator ParseBinaryOperator(string strBinaryOperator)
        {
            BinaryOperator result = BinaryOperator.unknown;
            Dictionary<string, BinaryOperator> map = new Dictionary<string, BinaryOperator>()
            {
                { "+" , BinaryOperator.add},
                { "-" , BinaryOperator.sub},
                { "*" , BinaryOperator.mul},
                { "/" , BinaryOperator.div},
                { ">" , BinaryOperator.great_than},
                { "<" , BinaryOperator.less_than},
                { ">=" , BinaryOperator.great_equal_than},
                { "<=" , BinaryOperator.less_equal_than},
                { "==" , BinaryOperator.equal_to},
                { "=" , BinaryOperator.assign},
                { "!=" , BinaryOperator.not_equal_to},
            };
            map.TryGetValue(strBinaryOperator, out result);
            return result;
        }

        private Statement ParseStatement(TokenVisitor tokenVisitor)
        {
            Statement statement = new Statement();
            string token = tokenVisitor.peek();
            if (token == "if")
            {
                statement = ParseStatementIf(tokenVisitor);
            }
            else if (token == "for")
            {
                statement = ParseStatementFor(tokenVisitor);
            }
            else
            {
                statement = ParseSimpleStatement(tokenVisitor);
            }

            return statement;
        }

        private StatementCallFunction ParseStatementCallFunction(TokenVisitor tokenVisitor)
        {
            StatementCallFunction statementCallFunction = new StatementCallFunction();
            string functionName = tokenVisitor.next();
            statementCallFunction.functionName = functionName;            

            Debug.Assert(tokenVisitor.next() == "(");

            while (tokenVisitor.peek() != ")")
            {
                string strVariable = tokenVisitor.next();
                Variable variable = ParseVariable(strVariable);
                statementCallFunction.inVariables.Add(variable);
                SkipFlag(tokenVisitor, ",");
            }
            tokenVisitor.next();
            return statementCallFunction;
        }

        private ExpressionBinary ParseStatementBinary(TokenVisitor tokenVisitor)
        {
            string left = tokenVisitor.next();
            string op = tokenVisitor.next();
            string right = tokenVisitor.next();

            BinaryOperator binaryOp = ParseBinaryOperator(op);
            Variable leftVariable = ParseVariable(left);
            Variable rightVariable = ParseVariable(right);
            return new ExpressionBinary(binaryOp, leftVariable.ToStatement(), rightVariable.ToStatement());
        }

        private Variable ParseVariable(string strVariable)
        {
            if (strVariable.Length == 0)
            {
                return Variable.None.Copy();
            }
            if (strVariable[0] == '\"')
            {
                return Variable.FromConstString(strVariable.Trim('\"'));
            }
            else if (strVariable == "true")
            {
                return Variable.FromConstBool(true);
            }
            else if (strVariable == "false")
            {
                return Variable.FromConstBool(false);
            }
            else if (strVariable[0] == '-' || (strVariable[0] >= '0' && strVariable[0] <= '9'))
            {
                return Variable.FromConstInt(Int64.Parse(strVariable));
            }
            else
            {
                return Variable.FromName(strVariable);
            }
        }

        
        private Statement ParseSimpleStatement(TokenVisitor tokenVisitor)
        {
            string token = tokenVisitor.peek();
            string nextNextToken = tokenVisitor.peek(1);
            TypeNotNoneName typeName = TypeNotNoneName.Unknown;
            if (token == "{")
            {
                // 11. {
                StatementScope statementScope = new StatementScope();
                tokenVisitor.next();
                while (tokenVisitor.peek() != "}")
                {
                    statementScope.AddSubStatement(ParseStatement(tokenVisitor));
                    SkipFlag(tokenVisitor, ";");
                }
                tokenVisitor.next();
                return statementScope;
            }
            else if (token == "break")
            {
                // 5. "break";
                tokenVisitor.next();
                return Variable.None.Copy().WithFlag(ControlFlowFlag.For_Break).ToStatement();
            }
            else if (token == "return")
            {
                // 4. "return" VARIABLE;
                tokenVisitor.next();
                string strVariable = tokenVisitor.next();
                return ParseVariable(strVariable).WithFlag(ControlFlowFlag.Return).ToStatementConstVariable();
            }
            else if ((typeName = ConvertTypeToTypeName(token)) != TypeNotNoneName.Unknown)
            {
                // 1.TYPE VARIABLE_NAME;
                tokenVisitor.next();
                string name = tokenVisitor.next();
                StatementDeclareVariable statementDeclareVariable = new StatementDeclareVariable(name, typeName);
                return statementDeclareVariable;
            }
            else if (nextNextToken == "=")
            {

                // 8. VARiABLE_NAME = VARIABLE
                // 7. VARIABLE_NAME = FUNCTION_NAME((VARIABLE,) *)
                // 2. VARIABLE_NAME = VARIABLE + OP + VARIABLE; // a= b + 1
                int totalCount = tokenVisitor.totalCount;
                int remainCount = tokenVisitor.remainCount;
                string next4Token = "";
                if (remainCount >= 4)
                {
                    next4Token = tokenVisitor.peek(3);
                }
                BinaryOperator binaryOperator = ParseBinaryOperator(next4Token);
                if (binaryOperator != BinaryOperator.unknown)
                {
                    // 2 VARIABLE_NAME = VARIABLE + OP + VARIABLE; // a= b + 1
                    tokenVisitor.next();
                    Variable leftVariable = ParseVariable(token);
                    Debug.Assert(tokenVisitor.next() == "=");
                    Statement rightStatement = ParseStatementBinary(tokenVisitor);
                    return new ExpressionBinary(BinaryOperator.assign, leftVariable.ToStatement(), rightStatement);
                }
                else if (next4Token == "(")
                {
                    // 7 VARIABLE_NAME = FUNCTION_NAME((VARIABLE,)*)
                    tokenVisitor.next();
                    Variable leftVariable = ParseVariable(token);
                    Debug.Assert(tokenVisitor.next() == "=");
                    Statement rightStatement = ParseStatementCallFunction(tokenVisitor);
                    return new ExpressionBinary(BinaryOperator.assign, leftVariable.ToStatement(), rightStatement);
                }
                else
                {
                    // 8 VARiABLE_NAME = VARIABLE
                    tokenVisitor.next();
                    Variable leftVariable = ParseVariable(token);
                    Debug.Assert(tokenVisitor.next() == "=");
                    token = tokenVisitor.next();
                    Variable rightVariable = ParseVariable(token);
                    return new ExpressionBinary(BinaryOperator.assign, leftVariable.ToStatement(), rightVariable.ToStatement());
                }
            }
            else if (nextNextToken == "(")
            {
                // 6. FUNCTION_NAME((VARIABLE,)*)
                return ParseStatementCallFunction(tokenVisitor);
            }
            else
            {
                // 3. VARIABLE + OP + VARIABLE; 
                return ParseStatementBinary(tokenVisitor);
            }

        }

        private Statement ParseStatementIf(TokenVisitor tokenVisitor)
        {
            StatementIf statementIf = new StatementIf();
            string token = tokenVisitor.next();
            Debug.Assert(tokenVisitor.next() == "(");
            statementIf.condition = ParseSimpleStatement(tokenVisitor);            
            Debug.Assert(tokenVisitor.next() == ")");           

            Debug.Assert(tokenVisitor.next() == "{");            
            while (tokenVisitor.peek() != "}")
            {
                statementIf.trueStatement.AddSubStatement(ParseStatement(tokenVisitor));
                SkipFlag(tokenVisitor, ";");
            }            
            Debug.Assert(tokenVisitor.next() == "}");
            if (tokenVisitor.peek() == "else")
            {
                tokenVisitor.next();
                Debug.Assert(tokenVisitor.next() == "{");                
                while (tokenVisitor.peek() != "}")
                {
                    statementIf.falseStatement.AddSubStatement(ParseStatement(tokenVisitor));
                    SkipFlag(tokenVisitor, ";");
                }                
                tokenVisitor.next();
            }            
            return statementIf;
        }
        private Statement ParseStatementFor(TokenVisitor tokenVisitor)
        {
            StatementFor statementFor = new StatementFor();
            string token = tokenVisitor.next();
            Debug.Assert(tokenVisitor.next() == "(");
            statementFor.for_begin_statement = ParseSimpleStatement(tokenVisitor);
            Debug.Assert(tokenVisitor.next() == ";");
            statementFor.for_condition_expression = ParseSimpleStatement(tokenVisitor);
            Debug.Assert(tokenVisitor.next() == ";");
            statementFor.for_step_statement = ParseSimpleStatement(tokenVisitor);
            Debug.Assert(tokenVisitor.next() == ")");
            Debug.Assert(tokenVisitor.next() == "{");            
            while (tokenVisitor.peek() != "}")
            {
                Statement statement = ParseStatement(tokenVisitor);
                statementFor.for_body_statement.AddSubStatement(statement);
                SkipFlag(tokenVisitor, ";");
            }            
            Debug.Assert(tokenVisitor.next() == "}");
            return statementFor;
        }   

        private  TypeNotNoneName ConvertTypeToTypeName(string strType)
        {
            TypeNotNoneName type = TypeNotNoneName.Unknown;
            if (strType == "int")
            {
                type = TypeNotNoneName.Int;
            }
            else if (strType == "double")
            {
                type = TypeNotNoneName.Double;
            }
            else if (strType == "string")
            {
                type = TypeNotNoneName.String;
            }
            else if (strType == "bool")
            {
                type = TypeNotNoneName.Bool;
            }
            return type;
        }

        private KeyValuePair<TypeNotNoneName, string> ParseDeclare(TokenVisitor tokenVisitor)
        {
            TypeNotNoneName type = TypeNotNoneName.Unknown;
            string strType = tokenVisitor.next();
            type = ConvertTypeToTypeName(strType);
            Debug.Assert(type != TypeNotNoneName.Unknown);
            string name = tokenVisitor.next();
            return new KeyValuePair<TypeNotNoneName,string>(type, name);
        }

        private StatementFunction ParseFunction(TokenVisitor tokenVisitor)
        {
            StatementFunction statementFunction = new StatementFunction();

            tokenVisitor.next();    // func
            statementFunction.functionName = tokenVisitor.next();    // name
            Debug.Assert(tokenVisitor.next() == "(");
            // ) or PARAM
            string token = "";
            while ((token = tokenVisitor.peek()) != ")")
            {
                if (token == ",")
                {
                    tokenVisitor.next();
                }
                KeyValuePair<TypeNotNoneName, string> pair = ParseDeclare(tokenVisitor);
                statementFunction.inParamNames.Add(pair.Value);
            }
            tokenVisitor.next();
            Debug.Assert(tokenVisitor.next() == "{");
            while (tokenVisitor.peek() != "}")
            {
                Statement statement = ParseStatement(tokenVisitor);
                statementFunction.AddSubStatement(statement);
                SkipFlag(tokenVisitor, ";");
            }
            Debug.Assert(tokenVisitor.next() == "}");
            return statementFunction;
        }

        private void SkipFlag(TokenVisitor tokenVisitor, String flag)
        {
            while(true)
            {
                string nextToken = tokenVisitor.peek();
                if (nextToken.Length == 0)
                {
                    break;
                }
                if (nextToken == flag)
                {
                    tokenVisitor.next();
                }
                else
                {
                    break;
                }
            }
        }
  
        

        private List<string> SouceStringToTokens(string content)
        {
            foreach(var pair in new List<KeyValuePair<string, string>>() {
                new KeyValuePair<string, string>("\r", ""),
                new KeyValuePair<string, string>("\n", ""),
                new KeyValuePair<string, string>("\t", " "),
                new KeyValuePair<string, string>("+", " + "),
                new KeyValuePair<string, string>("-", " - "),
                new KeyValuePair<string, string>("*", " * "),
                new KeyValuePair<string, string>("/", " / "),
                new KeyValuePair<string, string>(">", " > "),
                new KeyValuePair<string, string>("<", " < "),
                new KeyValuePair<string, string>("=", " = "),
                new KeyValuePair<string, string>("!", " ! "),
                new KeyValuePair<string, string>("(", " ( "),
                new KeyValuePair<string, string>(")", " ) "),
                new KeyValuePair<string, string>("{", " { "),
                new KeyValuePair<string, string>("}", " } "),
                new KeyValuePair<string, string>(";", " ; "),
                new KeyValuePair<string, string>(",", " , "),
                new KeyValuePair<string, string>(" =  = ", " == "),
                new KeyValuePair<string, string>(" >  = ", " >= "),
                new KeyValuePair<string, string>(" <  = ", " <= "),
                new KeyValuePair<string, string>(" !  = ", " != "),
            })
            {
                content = content.Replace(pair.Key, pair.Value);
            }                
            var tokens = content.Split(" ").Where(s => s.Length > 0).ToList();
            return tokens;
        }
        public ParseResult Parse()
        {   
            List<string> tokens = SouceStringToTokens(souce_content);
            TokenVisitor tokenVisitor = new TokenVisitor() { tokens = tokens };            
            ParseResult parseResult = new ParseResult();

            while (true)
            {
                string nextToken = tokenVisitor.peek();
                if (nextToken.Length == 0)
                {
                    break;
                }
                if (nextToken == "func")
                {
                    StatementFunction statementFunction = ParseFunction(tokenVisitor);
                    parseResult.dicParsedFunctions[statementFunction.functionName] = statementFunction;
                }
                else
                {
                    Statement subStatement = ParseStatement(tokenVisitor);
                    parseResult.statement.AddSubStatement(subStatement);
                    SkipFlag(tokenVisitor, ";");
                }                
            }
            return parseResult;
        }

        private TokenVisitor ConvertStringToTokenVisitor(string content)
        {
            List<String> tokens = SouceStringToTokens(content);
            TokenVisitor tokenVisitor = new TokenVisitor() { tokens = tokens};
            return tokenVisitor;
        }

        public void UnitTest()
        {
            StatementFunction statementFunction = ParseFunction(ConvertStringToTokenVisitor("func fname(int i, int j){i+2;j+3;return 5;}"));            
            ParseSimpleStatement(ConvertStringToTokenVisitor("int i;string j"));
            ParseSimpleStatement(ConvertStringToTokenVisitor("i = j / 5"));
            ParseSimpleStatement(ConvertStringToTokenVisitor("j / 5"));
            ParseSimpleStatement(ConvertStringToTokenVisitor("return j"));
            ParseSimpleStatement(ConvertStringToTokenVisitor("break;"));
            ParseSimpleStatement(ConvertStringToTokenVisitor("a=fname(3,j);"));
            ParseSimpleStatement(ConvertStringToTokenVisitor("i=j"));

            
            

            ParseStatementFor(ConvertStringToTokenVisitor("for(i=0; i < 5; i =i+1){a = a + i;b=5}"));
            ParseStatementIf(ConvertStringToTokenVisitor("if(i < 6){i = 91;}else{for(i=0;i<6;i=5){i=9;}}"));


        }
    }
}
